using System;
using System.IO;
using System.Runtime.Serialization;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ThePresenter.Windows;

namespace ThePresenter
{
	[Serializable]
	public sealed class SlideBackground : ISerializable, IDisposable, ICloneable
	{
		#region Constants

		/// <summary>
		/// The name of the <see cref="Bytes"/> property.
		/// </summary>
		const string BYTES = "Bytes";

		#endregion

		#region Fields

		Brush _Brush;

		bool _Disposed;

		#endregion

		#region Properties

		/// <summary>
		/// Gets the brush.
		/// </summary>
		/// <value>The brush.</value>
		public Brush Brush
		{
			get
			{
				return _Brush;
			}
		}

		/// <summary>
		/// Gets the bytes.
		/// </summary>
		/// <value>The bytes.</value>
		public byte[] Bytes
		{
			get
			{
				if (_Brush is ImageBrush)
				{
					var brush = _Brush as ImageBrush;
					var image = brush.ImageSource as BitmapImage;
					var length = (int)image.StreamSource.Length;
					var buffer = new byte[length];

					image.StreamSource.Position = 0;
					image.StreamSource.Read(buffer, 0, length);

					return buffer;
				}
				else if (_Brush is SolidColorBrush)
				{
					var brush = _Brush as SolidColorBrush;
					var color = brush.Color;

					return new byte[4] { color.A, color.R, color.G, color.B };
				}

				return null;
			}
		}

		/// <summary>
		/// Gets the string.
		/// </summary>
		/// <value>The string.</value>
		public string String
		{
			get
			{
				if (_Brush is ImageBrush)
				{
					return Convert.ToBase64String(Bytes);
				}
				else if (_Brush is SolidColorBrush)
				{
					var color = (_Brush as SolidColorBrush).Color;
					//return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.A, color.R, color.G, color.B);
					return color.ToString();
				}

				return null;
			}
		}

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="SlideBackground"/> class.
		/// </summary>
		/// <param name="info">The info.</param>
		/// <param name="context">The context.</param>
		/// <exception cref="System.ArgumentNullException">info is null.</exception>
		public SlideBackground(SerializationInfo info, StreamingContext context)
		{
			if (info == null)
				throw new ArgumentNullException("info");

			var bytes = (byte[])info.GetValue(BYTES, typeof(byte[]));
			var instance = FromBytes(bytes);

			_Brush = ((SlideBackground)instance.Clone())._Brush;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SlideBackground"/> class.
		/// </summary>
		/// <param name="color">The color.</param>
		public SlideBackground(Color color)
		{
			_GenerateBrush(color);
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SlideBackground"/> class.
		/// </summary>
		/// <param name="image">The image.</param>
		public SlideBackground(BitmapImage image)
		{
			_GenerateBrush(image);
		}

		#endregion

		//#region Destructors

		///// <summary>
		///// Releases unmanaged resources and performs other cleanup operations before the
		///// <see cref="SlideBackground"/> is reclaimed by garbage collection.
		///// </summary>
		//~SlideBackground()
		//{
		//    Dispose(false);
		//}

		//#endregion

		#region Methods

		/// <summary>
		/// Returns new instance of <see cref="SlideBackground"/> generated from <see cref="System.Byte"/> array.
		/// </summary>
		/// <param name="bytes">The <see cref="System.Byte"/> array.</param>
		/// <returns>New instance of <see cref="SlideBackground"/>.</returns>
		public static SlideBackground FromBytes(byte[] bytes)
		{
			if (bytes == null)
				throw new ArgumentNullException("bytes");

			if (bytes.Length == 4) // 32bit A-RGB Color
			{
				var color = Color.FromArgb(bytes[0], bytes[1], bytes[2], bytes[3]);

				return new SlideBackground(color);
			}
			else // thread as BitmapImage
			{
				var ms = new MemoryStream(bytes);
				var image = Helpers.GetBitmapImageFromStream(ms);

				return new SlideBackground(image);
			}
		}

		/// <summary>
		/// Returns new instance of <see cref="SlideBackground"/> generated from <see cref="System.String"/>.
		/// </summary>
		/// <param name="str">The <see cref="System.String"/>.</param>
		/// <returns>New instance of <see cref="SlideBackground"/>.</returns>
		public static SlideBackground FromString(string str)
		{
			if (str == null)
				throw new ArgumentNullException("str");

			if (str == string.Empty)
				throw new ArgumentException("Value cannot be empty string.", "str");

			if (str.StartsWith("#")) // Hex color
			{
				var color = Helpers.GetColorFromHexString(str);

				return new SlideBackground(color);
			}
			else // base64 encoded string
			{
				var bytes = Convert.FromBase64String(str);

				return FromBytes(bytes);
			}
		}

		/// <summary>
		/// Generates the brush.
		/// </summary>
		/// <param name="color">The color.</param>
		private void _GenerateBrush(Color color)
		{
			_Brush = new SolidColorBrush(color);
		}

		/// <summary>
		/// Generates the brush.
		/// </summary>
		/// <param name="image">The image.</param>
		private void _GenerateBrush(BitmapImage image)
		{
			_Brush = new ImageBrush(image);
		}

		#endregion

		#region ISerializable Members

		/// <summary>
		/// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
		/// </summary>
		/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
		/// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
		/// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			if (info == null)
				throw new ArgumentNullException("info");

			info.AddValue(BYTES, Bytes);
		}

		#endregion

		#region IDisposable Members

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <exception cref="System.InvalidOperationException">When already disposed.</exception>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <exception cref="System.InvalidOperationException">When already disposed.</exception>
		private void Dispose(bool disposing)
		{
			if (!_Disposed)
			{
				if (disposing)
				{
					if (_Brush is ImageBrush)
					{
						var brush = _Brush as ImageBrush;
						var image = brush.ImageSource as BitmapImage;

						image.StreamSource.Dispose();
						image = null;
					}
				}

				_Disposed = true;
			}
		}

		#endregion

		#region ICloneable Members

		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		public object Clone()
		{
			return FromBytes(Bytes);
		}

		#endregion
	}
}
